package com.cabbagetofu.crowd.controller;

import com.atguigu.crowd.constant.CrowdConstant;
import com.atguigu.crowd.until.CrowdUtil;
import com.atguigu.crowd.until.ResultEntity;
import com.cabbagetofu.api.MySQLRemoteService;
import com.cabbagetofu.api.RedisRemoteService;
import com.cabbagetofu.crowd.config.ShortMessageProperties;
import com.cabbagetofu.crowd.entity.po.MemberPO;
import com.cabbagetofu.crowd.entity.vo.MemberLoginVO;
import com.cabbagetofu.crowd.entity.vo.MemberVO;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpSession;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Controller
public class MemberController {

    @Autowired
    private ShortMessageProperties messageProperties;

    @Autowired
    private RedisRemoteService redisRemoteService;

    @Autowired
    private MySQLRemoteService mySQLRemoteService;

    /**
     * 执行退出登录，清空session后，返回首页
     * @param httpSession
     * @return
     */
    @RequestMapping("/auth/member/logout")
    public String logout(HttpSession httpSession){

        // 清空session
        httpSession.invalidate();

        // 在两个不同的网站，浏览器工作时不会使用相同的cookie，也就使不同微服务之间无法很好地同步数据。因此分布式项目中重定向时，需要带上前缀
        // 退出登陆后返回首页
        return "redirect:http://localhost/";
    }


    /**
     * 执行登录，核对表单数据无误后，并将用户信息存入session域中，即可登录成功
     * @param loginAcct
     * @param userPswd
     * @param modelMap
     * @param httpSession
     * @return  会员中心页面
     */
    @RequestMapping("/auth/member/do/login")
    public String loginMember(@RequestParam("loginacct")String loginAcct, @RequestParam("userpswd")String userPswd,
                              ModelMap modelMap, HttpSession httpSession){

        // 根据用户填写的账号查询到对应的memberPO对象
        ResultEntity<MemberPO> result = mySQLRemoteService.getMemberPOByLoginAcctRemote(loginAcct);

        // 如果查询失败，则返回登陆页面重新登录
        if (ResultEntity.FAILED.equals(result.getResult())){

            modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, result.getMessage());
            return "member-login";
        }

        // 获取查询到的对象
        MemberPO memberPO = result.getData();
        // 如果查询不到相关信息，则返回登陆页面重新登录
        if (memberPO ==null){

            modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, CrowdConstant.MESSAGE_LOGIN_FAILED);
            return "member-login";
        }

        // 将用户填写的密码与数据库中的密码比较
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        boolean matchesResult = passwordEncoder.matches(userPswd, memberPO.getUserPswd());
        if (!matchesResult){

            modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, CrowdConstant.MESSAGE_LOGIN_FAILED);
            return "member-login";
        }

        // 所有信息核对无误，通过MemberLoginVO对象保存必要的三个值存入至session域中即可
        MemberLoginVO memberLoginVO = new MemberLoginVO(memberPO.getId(), memberPO.getUserName(), memberPO.getEmail());

        // 登录成功后，将用户信息保存至session域中
        httpSession.setAttribute(CrowdConstant.ATTR_NAME_LOGIN_MEMBER, memberLoginVO);

        // 在两个不同的网站，浏览器工作时不会使用相同的cookie，也就使不同微服务之间无法很好地同步数据。因此分布式项目中重定向时，需要带上前缀
        // 重定向至会员中心页面
        return "redirect:http://localhost/auth/member/to/center/page";

    }

    /**
     * 根据注册页面提交的表单信息，将用户填写的信息存入至数据库中
     * @param memberVO
     * @param modelMap
     * @return
     */
    @RequestMapping("/auth/do/member/register")
    public String registerMember(MemberVO memberVO, ModelMap modelMap){

        // 获取注册手机号
        String phoneNum = memberVO.getPhoneNum();

        // 拼接Redis中的key值
        String key = CrowdConstant.REDIS_CODE_PREFIX + phoneNum;

        // 查询redis中是否有对应的验证码
        ResultEntity<String> result = redisRemoteService.getRedisStringValueByKey(key);

        // 如果查询不到对应的号码，则直接返回注册页面
        if (ResultEntity.FAILED.equals(result)){

            modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, result.getMessage());
            return "member-reg";
        }

        // 如果验证码不存在，返回注册页面
        if (result.getData() == null){

            modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, CrowdConstant.MESSAGE_CODE_NOT_EXIST);
            return "member-reg";
        }

        // 比较表单中的验证码和redis中的验证码,不相等返回注册页面
        if (!Objects.equals(result.getData(), memberVO.getCode())){

            modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, CrowdConstant.MESSAGE_CODE_INVALID);
            return "member-reg";
        }

        // 验证码不为空，并且一致
        // 删除redis中的值
        redisRemoteService.removeRedisStringValueByKey(key);

        // 将用户注册的密码加密
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String userPswdBefore = memberVO.getUserPswd();
        String userPswdafter = passwordEncoder.encode(userPswdBefore);
        memberVO.setUserPswd(userPswdafter);

        // 保存用户的注册信息
        MemberPO memberPO = new MemberPO();
        BeanUtils.copyProperties(memberVO, memberPO);
        ResultEntity<String> saveResult = mySQLRemoteService.saveMember(memberPO);

        // 如果保存失败，返回注册页面
        if (saveResult.getResult().equals(ResultEntity.FAILED)){

            modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, saveResult.getMessage());
            return "member-reg";
        }

        // 在两个不同的网站，浏览器工作时不会使用相同的cookie，也就使不同微服务之间无法很好地同步数据。因此分布式项目中重定向时，需要带上前缀
        // 注册成功后，重定向至登录页
        return "redirect:http://localhost/auth/member/to/login/page";

    }


    /**
     * 给用户的手机发送验证码，并将验证码存入Redis中
     * @param phoneNum
     * @return
     */
    @ResponseBody
    @RequestMapping("/auth/member/send/short/message.json")
    public ResultEntity<String> sendShortMessage(@RequestParam("phoneNum") String phoneNum){

        //  调用工具类中的发送验证码的方法，可以从配置文件中读取配置的接口信息
        ResultEntity<String> resultEntity = CrowdUtil.sendShortMessage(messageProperties.getHost(),
                                                    messageProperties.getPath(),
                                                    messageProperties.getMethod(), messageProperties.getAppcode(),
                                                    phoneNum,messageProperties.getSmsTemplateNo(),
                                                    messageProperties.getSmsSignId());

        // 判断发送是否成功
        if (ResultEntity.SUCCESS.equals(resultEntity.getResult())){

            // 获取发送的验证码
            String code = resultEntity.getData();

            // 将发送的验证码存入redis，过期时间为5分钟
            ResultEntity<String> saveResultEntity = redisRemoteService.setRedisKeyValueRemoteWithTimeout(
                    CrowdConstant.REDIS_CODE_PREFIX + phoneNum, code,
                    5, TimeUnit.MINUTES);

            // 判断存入redis是否成功
            if (saveResultEntity.getResult().equals(ResultEntity.SUCCESS)){
                // 存入成功，返回成功
                return ResultEntity.successWithoutData();

            }
            // 存入失败，返回redis返回的结果
            return saveResultEntity;

        }

        // 发送验证码失败，直接返回发送验证码函数的结果
        return resultEntity;

    }

}
